﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using WG3000_COMM.Common;
using WG3000_COMM.Core;

namespace WgCtrlService
{
    public partial class WatchServer : ServiceBase
    {
        public WatchServer()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {

            bindIpAddressPort = "61005";


            LogHelper.WriteLog("服务初始化....");

            string hostName = Dns.GetHostName();
            bool flag = false;
            foreach (IPAddress address in Dns.GetHostEntry(hostName).AddressList)
            {
                if (((address.AddressFamily == AddressFamily.InterNetwork) && !address.IsIPv6LinkLocal) && (address.ToString() != "127.0.0.1"))
                {
                    if (flag)
                    {
                        LogHelper.WriteLog("电脑存在多个IP, 建议前期开发时只使用一个IP操作.  [假如无线与网线口同时在用时, 请关键无线口]", new Exception { });
                        break;
                    }
                    flag = true;
                    bindIpAddressIp = address.ToString();
                }
            }
            if (!flag)
            {
                LogHelper.WriteLog("网络不通! 请接好网线.", new Exception { });
            }


            LogHelper.WriteLog("系统监听程序获取绑定IP:"+bindIpAddressIp);

            this.WatchingServerRuning(bindIpAddressIp, int.Parse(this.bindIpAddressPort));

        } 

        protected override void OnStop()
        {
            if (!this.bStopWatchServer)
            {
                this.bStopWatchServer = true;

                ctSource.Cancel();

                Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(500);
                    ctSource.Cancel();
                });

                try
                {
                    reciveTask.Wait();

                    this.udpserverLast.Close(); 

                    LogHelper.WriteLog("任务已经停止，监听服务已经关闭。");
                }
                catch (AggregateException e)
                {
                    if (e.InnerException is OperationCanceledException)
                    {
                        LogHelper.WriteLog("任务已经停止", e);
                    }
                    else
                    {
                        LogHelper.WriteLog("任务停止错误", e); 
                    }
                }

            }
            
            
        }


        #region 基本变量

        private string bindIpAddressIp;
        private string bindIpAddressPort;
        private bool bStopWatchServer = false;
        private wgUdpServerCom udpserverLast; 

        private Task reciveTask;

        private CancellationTokenSource ctSource = new CancellationTokenSource();

        private string[] RecordDetails = new string[] {
            "1", "SwipePass", "Swipe", "刷卡开门", "2", "SwipePass", "Swipe Close", "刷卡关", "3", "SwipePass", "Swipe Open", "刷卡开", "4", "SwipePass", "Swipe Limited Times", "刷卡开门(带限次)",
            "5", "SwipeNOPass", "Denied Access: PC Control", "刷卡禁止通过: 电脑控制", "6", "SwipeNOPass", "Denied Access: No PRIVILEGE", "刷卡禁止通过: 没有权限", "7", "SwipeNOPass", "Denied Access: Wrong PASSWORD", "刷卡禁止通过: 密码不对", "8", "SwipeNOPass", "Denied Access: AntiBack", "刷卡禁止通过: 反潜回",
            "9", "SwipeNOPass", "Denied Access: More Cards", "刷卡禁止通过: 多卡", "10", "SwipeNOPass", "Denied Access: First Card Open", "刷卡禁止通过: 首卡", "11", "SwipeNOPass", "Denied Access: Door Set NC", "刷卡禁止通过: 门为常闭", "12", "SwipeNOPass", "Denied Access: InterLock", "刷卡禁止通过: 互锁",
            "13", "SwipeNOPass", "Denied Access: Limited Times", "刷卡禁止通过: 受刷卡次数限制", "14", "SwipeNOPass", "Denied Access: Limited Person Indoor", "刷卡禁止通过: 门内人数限制", "15", "SwipeNOPass", "Denied Access: Invalid Timezone", "刷卡禁止通过: 卡过期或不在有效时段", "16", "SwipeNOPass", "Denied Access: In Order", "刷卡禁止通过: 按顺序进出限制",
            "17", "SwipeNOPass", "Denied Access: SWIPE GAP LIMIT", "刷卡禁止通过: 刷卡间隔约束", "18", "SwipeNOPass", "Denied Access", "刷卡禁止通过: 原因不明", "19", "SwipeNOPass", "Denied Access: Limited Times", "刷卡禁止通过: 刷卡次数限制", "20", "ValidEvent", "Push Button", "按钮开门",
            "21", "ValidEvent", "Push Button Open", "按钮开", "22", "ValidEvent", "Push Button Close", "按钮关", "23", "ValidEvent", "Door Open", "门打开[门磁信号]", "24", "ValidEvent", "Door Closed", "门关闭[门磁信号]",
            "25", "ValidEvent", "Super Password Open Door", "超级密码开门", "26", "ValidEvent", "Super Password Open", "超级密码开", "27", "ValidEvent", "Super Password Close", "超级密码关", "28", "Warn", "Controller Power On", "控制器上电",
            "29", "Warn", "Controller Reset", "控制器复位", "30", "Warn", "Push Button Invalid: Disable", "按钮不开门: 按钮禁用", "31", "Warn", "Push Button Invalid: Forced Lock", "按钮不开门: 强制关门", "32", "Warn", "Push Button Invalid: Not On Line", "按钮不开门: 门不在线",
            "33", "Warn", "Push Button Invalid: InterLock", "按钮不开门: 互锁", "34", "Warn", "Threat", "胁迫报警", "35", "Warn", "Threat Open", "胁迫报警开", "36", "Warn", "Threat Close", "胁迫报警关",
            "37", "Warn", "Open too long", "门长时间未关报警[合法开门后]", "38", "Warn", "Forced Open", "强行闯入报警", "39", "Warn", "Fire", "火警", "40", "Warn", "Forced Close", "强制关门",
            "41", "Warn", "Guard Against Theft", "防盗报警", "42", "Warn", "7*24Hour Zone", "烟雾煤气温度报警", "43", "Warn", "Emergency Call", "紧急呼救报警", "44", "RemoteOpen", "Remote Open Door", "操作员远程开门",
            "45", "RemoteOpen", "Remote Open Door By USB Reader", "发卡器确定发出的远程开门"
        };
        private string getReasonDetailChinese(int Reason)
        {
            if (Reason > 0x2d)
            {
                return "";
            }
            if (Reason <= 0)
            {
                return "";
            }
            return this.RecordDetails[((Reason - 1) * 4) + 3];
        }
        #endregion


        /// <summary>
        /// 启动监听服务
        /// </summary>
        /// <param name="watchServerIP"></param>
        /// <param name="watchServerPort"></param>
        /// <returns>1启动成功，-1启动失败</returns>
        private int WatchingServerRuning(string watchServerIP, int watchServerPort)
        {
            try
            {
                wgUdpServerCom server = new wgUdpServerCom(watchServerIP, watchServerPort)
                {
                    IncludeIPInfo = true
                };
                if (!server.IsWatching())
                {
                    LogHelper.WriteLog("进入接收服务器监控状态....失败");
                    return -1;
                }

                this.udpserverLast = server;

                this.bStopWatchServer = false;

                LogHelper.WriteLog("进入接收服务器监控状态....");

                var source = new CancellationTokenSource();

                reciveTask = new Task(new Action(this.ListenDataReturn), ctSource.Token);

                reciveTask.Start();
                 
            }
            catch (Exception exception)
            {
                LogHelper.WriteLog("绑定IP，启动监听服务器失败", exception);
            }
            return 1;
        }

        /// <summary>
        /// 监听服务器返回值
        /// </summary>
        /// <param name="server"></param>
        private void ListenDataReturn()
        {
            long num = 0L;

            while (!this.bStopWatchServer)
            {
                if (this.udpserverLast.receivedCount() > 0)
                {
                    byte[] buff = this.udpserverLast.getRecords();
                    if (buff[1] == 0x20)
                    {
                        long num3 = this.byteToLong(buff, 4, 4);
                        if (wgControllerInfo.GetControllerType((int)num3) > 0)
                        { 
                            var sbStr = new StringBuilder($"接收到来自控制器SN = {num3} ,");

                            if (this.udpserverLast.IncludeIPInfo && ((buff.Length % 0x40) == 4))
                            {
                                sbStr.Append($"控制器IP = {buff[buff.Length - 4]:d}.{buff[buff.Length - 3]:d}.{buff[buff.Length - 2]:d}.{buff[buff.Length - 1]:d} ");
                            }

                            sbStr.Append("数据包..");

                            LogHelper.WriteLog(sbStr.ToString());

                            long num4 = this.byteToLong(buff, 8, 4);

                            if ((num < num4) || ((num4 - num) < -5L))
                            {
                                num = num4;
                                this.displayRecordInformation(buff);
                                this.dealSwipeRecord(buff, ref this.udpserverLast);
                            }
                        }
                    }
                    if (buff[1] == 0x22)
                    {
                        long num6 = this.byteToLong(buff, 4, 4);
                        if (wgControllerInfo.GetControllerType((int)num6) > 0)
                        {
                            var sbStr = new StringBuilder($"接收到来自控制器SN = {num6} 的二维码"); 

                            if (this.udpserverLast.IncludeIPInfo && ((buff.Length % 0x40) == 4))
                            {
                                sbStr.Append($"控制器IP = {buff[buff.Length - 4]:d}.{buff[buff.Length - 3]:d}.{buff[buff.Length - 2]:d}.{buff[buff.Length - 1]:d} 的二维码");
                            }
                            sbStr.Append("数据包..");
                            LogHelper.WriteLog(sbStr.ToString());
                            this.byteToLong(buff, 8, 4);
                            this.dealQRData(buff, ref this.udpserverLast);
                        }
                    }
                }
                else
                {
                    Thread.Sleep(100);
                }
            } 
        }
         

        /// <summary>
        /// 二维码开门-设备传送数据处理
        /// </summary>
        /// <param name="recv"></param>
        /// <param name="server"></param>
        private void dealQRData(byte[] recv, ref wgUdpServerCom server)
        {
            int length = 0;
            length = (int)this.byteToLong(recv, 8, 4);
            byte num1 = recv[13];
            int doorNO = recv[14];
            int inOrOut = recv[15];
            long num4 = this.byteToLong(recv, 40, 4);
            if (length >= 1)
            {
                byte[] destinationArray = new byte[length];
                Array.Copy(recv, 0x40, destinationArray, 0, length);
                LogHelper.WriteLog($"流水号={num4} 二维码原始数据:{BitConverter.ToString(destinationArray)}".Replace('-', ' ')+$"流水号={num4} 二维码原始数据(转换为字符串):{Encoding.GetEncoding("GB2312").GetString(destinationArray).Trim()}");
                //二维码直接转换-执行开门
                if (true)
                {
                    long cardNO = 0L;
                    cardNO = num4;
                    long sn = this.byteToLong(recv, 4, 4);
                    this.remoteOpenDoor(ref server, sn, doorNO, inOrOut, cardNO);
                }
            }
        }
        /// <summary>
        /// 普通接收数据，刷卡和模拟开门-设备传送数据处理
        /// </summary>
        /// <param name="recv"></param>
        /// <param name="server"></param>
        private void dealSwipeRecord(byte[] recv, ref wgUdpServerCom server)
        {
            this.byteToLong(recv, 8, 4);
            int num = recv[12];
            int num2 = recv[13];
            int doorNO = recv[14];
            int inOrOut = recv[15];
            long cardNO = 0L;
            cardNO = this.byteToLong(recv, 0x10, 4);
            long num6 = 0L;
            num6 = this.byteToLong(recv, 0x2c, 4);
            cardNO += num6 << 0x20;
            //$"{recv[20]:X2}{recv[0x15]:X2}-{recv[0x16]:X2}-{recv[0x17]:X2} {recv[0x18]:X2}:{recv[0x19]:X2}:{recv[0x1a]:X2}";
            byte num1 = recv[0x1b];
            if (((num == 1) && (num2 == 0)) && true)
            {
                long sn = this.byteToLong(recv, 4, 4);
                this.remoteOpenDoor(ref server, sn, doorNO, inOrOut, cardNO);
            }
        }
        /// <summary>
        /// 格式化返回数据结果
        /// </summary>
        /// <param name="recv"></param>
        private void displayRecordInformation(byte[] recv)
        {
            int num = 0;
            num = (int)this.byteToLong(recv, 8, 4);
            int num2 = recv[12];
            int num3 = recv[13];
            int num4 = recv[14];
            int num5 = recv[15];
            long num6 = 0L;
            num6 = this.byteToLong(recv, 0x10, 4);
            long num7 = 0L;
            num7 = this.byteToLong(recv, 0x2c, 4);
            num6 += num7 << 0x20;
            string str = "2000-01-01 00:00:00";
            str = $"{recv[20]:X2}{recv[0x15]:X2}-{recv[0x16]:X2}-{recv[0x17]:X2} {recv[0x18]:X2}:{recv[0x19]:X2}:{recv[0x1a]:X2}";
            int reason = recv[0x1b];
            switch (num2)
            {
                case 0:
                    LogHelper.WriteLog($"索引位={num}  无记录");
                    return;
                case 0xff:
                    LogHelper.WriteLog(" 指定索引位的记录已被覆盖掉了,请使用索引0, 取回最早一条记录的索引值");
                    return;
                case 1:
                    {
                        LogHelper.WriteLog($"索引位={num}  卡号 = {num6},门号 = {num4},进出 = {num5} 1进门 !=1出门,有效 = {num3} 1通过 !=1禁止,时间 = {str},描述 = {this.getReasonDetailChinese(reason)}");
                        return;
                    }
                case 2:
                    var sbStr = new StringBuilder($"索引位={num}  非刷卡记录"); 
                    if ((reason == 0x2c) && (num6 > 1L))
                    {
                        sbStr.Append($"  模拟卡号 = {num6}");
                    }
                    else
                    {
                        sbStr.Append($"  编号 = {num6}");
                    }
                    sbStr.Append($"  门号 = {num4}");
                    sbStr.Append($"  时间 = {str}");

                    LogHelper.WriteLog(sbStr.ToString());
                    return;

                case 3:
                    LogHelper.WriteLog($"索引位={num} 报警记录, 编号 = {num6},门号 = {num4},时间 = {str},描述 = {this.getReasonDetailChinese(reason)}"); 
                    break;
            }
        }  
        /// <summary>
        /// 操作开门指令
        /// </summary>
        /// <param name="serverCurrent">当前监听服务器</param>
        /// <param name="sn">设备序列号</param>
        /// <param name="doorNO">门号1-4</param>
        /// <param name="inOrOut">进出门</param>
        /// <param name="cardNO">卡号</param>
        /// <returns></returns>
        private int remoteOpenDoor(ref wgUdpServerCom serverCurrent, long sn, int doorNO, int inOrOut, long cardNO)
        {
            int num = -13;
            byte[] destinationArray = new byte[0x40];
            for (int i = 0; i < 0x40; i++)
            {
                destinationArray[i] = 0;
            }
            destinationArray[0] = 0x17;
            destinationArray[1] = 0x40;
            Array.Copy(BitConverter.GetBytes(sn), 0, destinationArray, 4, 4);
            destinationArray[8] = (byte)(doorNO & 0xff);
            destinationArray[0x1c] = (inOrOut == 1) ? ((byte)0) : ((byte)1);
            Array.Copy(BitConverter.GetBytes(cardNO), 0, destinationArray, 20, 4);
            Array.Copy(BitConverter.GetBytes(cardNO), 4, destinationArray, 0x18, 4);
            destinationArray[0x20] = 90;
            int num3 = 3;
            long sequenceId = 0L;
            while (num3 > 0)
            {
                serverCurrent.receivedReplyClear();
                sequenceId = serverCurrent.UDP_OnlySend(destinationArray, sequenceId);
                if (sequenceId >= 0L)
                {
                    LogHelper.WriteLog($"发出远程开门指令 控制器SN={sn.ToString()}, 门号= {doorNO.ToString()}, {(inOrOut == 1 ? "进门" : "出门") } 模拟卡号= {cardNO}, 流水号= 0x{sequenceId:X} ");
                }
                else
                {
                    LogHelper.WriteLog($"控制器 没有连接，失败: 发出远程开门指令 控制器SN={sn.ToString()}, 门号= {doorNO.ToString()}, {(inOrOut == 1 ? "进门" : "出门") } 模拟卡号= {cardNO} ");
                    return num;
                }
                int num5 = 100;
                while (num5-- > 0)
                {
                    if (this.udpserverLast.receivedReplyCount() > 0)
                    {
                        byte[] buff = this.udpserverLast.getReply();
                        if (this.byteToLong(buff, 40, 4) != sequenceId)
                        {
                            continue;
                        }
                        LogHelper.WriteLog((buff[8] == 1) ? "成功!" : "失败.. 收到返回 信息 " + BitConverter.ToString(buff)); 
                        if (buff[8] == 1)
                        {
                            num = 1;
                        }
                        break;
                    }
                    Thread.Sleep(10);
                }
                if ((num5 >= 0) || (num >= 0))
                {
                    break;
                }
                num3--;
            }
            if (num <= 0)
            {
                LogHelper.WriteLog($"控制器：{sn},门号：{doorNO}，卡号：{cardNO}，进出：{inOrOut} 失败...\r\n");
            }
            return num;
        }
        /// <summary>
        /// 截取数据流字节转换为long类型
        /// </summary>
        /// <param name="buff"></param>
        /// <param name="start"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        private long byteToLong(byte[] buff, int start, int len)
        {
            long num = 0L;
            for (int i = 0; (i < len) && (i < 4); i++)
            {
                long num3 = buff[i + start];
                num += num3 << (8 * i);
            }
            return num;
        }

    }
}
